#ifndef     __DMA_H
#define     __DMA_H

#include "./compat.h"
#include <utility>

namespace device::dma {

namespace CCR {

/**
 * @brief 
 * 0: 非存储器到存储器模式
 * 1: 存储器到存储器模式
 */
template <uint8_t>
inline constexpr uint32_t MEM2MEM = 0;

template <>
inline constexpr uint32_t MEM2MEM<0> = 0;

template <>
inline constexpr uint32_t MEM2MEM<1> = 1 << 14;

/**
 * @brief 
 * 0: 低优先级
 * 1: 中优先级
 * 2: 高优先级
 * 3: 最高优先级
 */
template <uint8_t>
inline constexpr uint32_t PL = 0;

template <>
inline constexpr uint32_t PL<0> = 0;

template <>
inline constexpr uint32_t PL<1> = 0x01 << 12;

template <>
inline constexpr uint32_t PL<2> = 0x02 << 12;

template <>
inline constexpr uint32_t PL<3> = 0x03 << 12;

/**
 * @brief 存储器数据宽度
 * 0: 8位
 * 1: 16位
 * 2: 32位
 */
template <uint8_t>
inline constexpr uint32_t MSIZE = 0;

template <>
inline constexpr uint32_t MSIZE<0> = 0;

template <>
inline constexpr uint32_t MSIZE<1> = 0x01 << 10;

template <>
inline constexpr uint32_t MSIZE<2> = 0x02 << 10;

/**
 * @brief 外设数据宽度
 * 0: 8位
 * 1: 16位
 * 2: 32位
 */
template <uint8_t>
inline constexpr uint32_t PSIZE = 0;

template <>
inline constexpr uint32_t PSIZE<0> = 0;

template <>
inline constexpr uint32_t PSIZE<1> = 0x01 << 8;

template <>
inline constexpr uint32_t PSIZE<2> = 0x02 << 8;

/**
 * @brief 
 * 0: 存储器地址不增加
 * 1: 存储器地址增加
 */
template <uint8_t>
inline constexpr uint32_t MINC = 0;

template <>
inline constexpr uint32_t MINC<0> = 0;

template <>
inline constexpr uint32_t MINC<1> = 0x01 << 7;

/**
 * @brief 
 * 0: 外设地址不增加
 * 1: 外设地址增加
 */
template <uint8_t>
inline constexpr uint32_t PINC = 0;

template <>
inline constexpr uint32_t PINC<0> = 0;

template <>
inline constexpr uint32_t PINC<1> = 0x01 << 6;

/**
 * @brief 
 * 0: 不执行循环操作
 * 1: 执行循环操作
 */
template <uint8_t>
inline constexpr uint32_t CIRC = 0;

template <>
inline constexpr uint32_t CIRC<0> = 0;

template <>
inline constexpr uint32_t CIRC<1> = 0x01 << 5;

/**
 * @brief 
 * 0: 从外设读
 * 1: 从存储器读
 */
template <uint8_t>
inline constexpr uint32_t DIR = 0;

template <>
inline constexpr uint32_t DIR<0> = 0;

template <>
inline constexpr uint32_t DIR<1> = 0x01 << 4;

/**
 * @brief 
 * 0: 禁止传输错误中断
 * 1: 允许传输错误种度
 */
template <uint8_t>
inline constexpr uint32_t TEIE = 0;

template <>
inline constexpr uint32_t TEIE<0> = 0;

template <>
inline constexpr uint32_t TEIE<1> = 0x01 << 3;

/**
 * @brief 
 * 0: 禁止半传输中断
 * 1: 允许半传输中断
 */
template <uint8_t>
inline constexpr uint32_t HTIE = 0;

template <>
inline constexpr uint32_t HTIE<0> = 0;

template <>
inline constexpr uint32_t HTIE<1> = 0x01 << 2;

/**
 * @brief 
 * 0: 禁止传输完成中断
 * 1: 允许传输完成中断
 */
template <uint8_t>
inline constexpr uint32_t TCIE = 0;

template <>
inline constexpr uint32_t TCIE<0> = 0;

template <>
inline constexpr uint32_t TCIE<1> = 0x01 << 1;

/**
 * @brief 
 * 0: 通道不工作
 * 1: 通道工作
 */
template <uint8_t>
inline constexpr uint32_t EN = 0;

template <>
inline constexpr uint32_t EN<0> = 0;

template <>
inline constexpr uint32_t EN<1> = 0x01;

}

inline constexpr uint32_t uart_recv_ccr_value = 
    CCR::EN<1> +
    CCR::TCIE<0> +
    CCR::HTIE<0> +
    CCR::TEIE<0> +
    CCR::DIR<0> +
    CCR::CIRC<1> +
    CCR::PINC<0> +
    CCR::MINC<1> +
    CCR::PSIZE<0> +
    CCR::MSIZE<0> +
    CCR::PL<0> +
    CCR::MEM2MEM<0>;

inline constexpr uint32_t uart_write_ccr_value =
    CCR::EN<1> +
    CCR::TCIE<1> +
    CCR::HTIE<0> +
    CCR::TEIE<0> +
    CCR::DIR<1> +
    CCR::CIRC<0> +
    CCR::PINC<0> +
    CCR::MINC<1> +
    CCR::PSIZE<0> +
    CCR::MSIZE<0> +
    CCR::PL<0> +
    CCR::MEM2MEM<0>;

template <uint32_t>
inline constexpr uint32_t DMA_BASE = DMA1_BASE;
template <uint32_t>
inline constexpr uint32_t DMA_CHAN_ID = 1;

template <>
inline constexpr uint32_t DMA_BASE<DMA1_Channel1_BASE> = DMA1_BASE;
template <>
inline constexpr uint32_t DMA_CHAN_ID<DMA1_Channel1_BASE> = 1;

template <>
inline constexpr uint32_t DMA_BASE<DMA1_Channel2_BASE> = DMA1_BASE;
template <>
inline constexpr uint32_t DMA_CHAN_ID<DMA1_Channel2_BASE> = 2;

template <>
inline constexpr uint32_t DMA_BASE<DMA1_Channel3_BASE> = DMA1_BASE;
template <>
inline constexpr uint32_t DMA_CHAN_ID<DMA1_Channel3_BASE> = 3;

template <>
inline constexpr uint32_t DMA_BASE<DMA1_Channel4_BASE> = DMA1_BASE;
template <>
inline constexpr uint32_t DMA_CHAN_ID<DMA1_Channel4_BASE> = 4;

template <>
inline constexpr uint32_t DMA_BASE<DMA1_Channel5_BASE> = DMA1_BASE;
template <>
inline constexpr uint32_t DMA_CHAN_ID<DMA1_Channel5_BASE> = 5;

template <>
inline constexpr uint32_t DMA_BASE<DMA1_Channel6_BASE> = DMA1_BASE;
template <>
inline constexpr uint32_t DMA_CHAN_ID<DMA1_Channel6_BASE> = 6;

template <>
inline constexpr uint32_t DMA_BASE<DMA1_Channel7_BASE> = DMA1_BASE;
template <>
inline constexpr uint32_t DMA_CHAN_ID<DMA1_Channel7_BASE> = 7;

template <>
inline constexpr uint32_t DMA_BASE<DMA2_Channel1_BASE> = DMA2_BASE;
template <>
inline constexpr uint32_t DMA_CHAN_ID<DMA2_Channel1_BASE> = 1;

template <>
inline constexpr uint32_t DMA_BASE<DMA2_Channel2_BASE> = DMA2_BASE;
template <>
inline constexpr uint32_t DMA_CHAN_ID<DMA2_Channel2_BASE> = 2;

template <>
inline constexpr uint32_t DMA_BASE<DMA2_Channel3_BASE> = DMA2_BASE;
template <>
inline constexpr uint32_t DMA_CHAN_ID<DMA2_Channel3_BASE> = 3;

template <>
inline constexpr uint32_t DMA_BASE<DMA2_Channel4_BASE> = DMA2_BASE;
template <>
inline constexpr uint32_t DMA_CHAN_ID<DMA2_Channel4_BASE> = 4;

template <>
inline constexpr uint32_t DMA_BASE<DMA2_Channel5_BASE> = DMA2_BASE;
template <>
inline constexpr uint32_t DMA_CHAN_ID<DMA2_Channel5_BASE> = 5;

template <uint32_t Base>
struct Channel {

    static constexpr uint32_t DmaBase = DMA_BASE<Base>;
    static constexpr uint32_t ChanId = DMA_CHAN_ID<Base>;

    /**
     * @brief 
     * 传输错误
     */
    static inline bool IsTE() {
        constexpr uint32_t mask = (0x01 << (4 * (ChanId - 1))) + 3; 
        return (reinterpret_cast<DMA_TypeDef *>(DmaBase)->ISR & mask) != 0;       
    }

    /**
     * @brief 
     * 传输过半
     */
    static inline bool IsHT() {
        constexpr uint32_t mask = (0x01 << (4 * (ChanId - 1))) + 2; 
        return (reinterpret_cast<DMA_TypeDef *>(DmaBase)->ISR & mask) != 0; 
    }

    /**
     * @brief 
     * 传输完成
     */
    static inline bool IsTC() {
        constexpr uint32_t mask = (0x01 << (4 * (ChanId - 1))) + 1; 
        return (reinterpret_cast<DMA_TypeDef *>(DmaBase)->ISR & mask) != 0; 
    }

    /**
     * @brief 
     * 全局中断
     */
    static inline bool IsGI() {
        constexpr uint32_t mask = (0x01 << (4 * (ChanId - 1))) + 0; 
        return (reinterpret_cast<DMA_TypeDef *>(DmaBase)->ISR & mask) != 0; 
    }

    /**
     * @brief 
     * 清楚所有标志位
     */
    static inline void ClrFlag() {
        constexpr uint32_t mask = (0x0F << (4 * (ChanId - 1))); 
        reinterpret_cast<DMA_TypeDef *>(DmaBase)->IFCR = mask;
    }

    static inline uint32_t NDTR() {
        return reinterpret_cast<DMA_Channel_TypeDef *>(Base)->CNDTR;
    }

    static inline void Translate(const void *buf, uint32_t len, uint32_t periph_addr, uint32_t ccr) {
        ClrFlag();
        reinterpret_cast<DMA_Channel_TypeDef *>(Base)->CCR = 0;
        reinterpret_cast<DMA_Channel_TypeDef *>(Base)->CMAR = reinterpret_cast<uint32_t>(buf);
        reinterpret_cast<DMA_Channel_TypeDef *>(Base)->CPAR = periph_addr;
        reinterpret_cast<DMA_Channel_TypeDef *>(Base)->CNDTR = len;
        reinterpret_cast<DMA_Channel_TypeDef *>(Base)->CCR = ccr;
    }

    static inline void WriteForUart(const void *buf, uint32_t len, uint32_t periph_addr) {
        Translate(buf, len, periph_addr, uart_write_ccr_value);
    }

    static inline void RecvForUart(void *buf, uint32_t len, uint32_t periph_addr) {
        Translate(buf, len, periph_addr, uart_recv_ccr_value);
    }
};

using D1C1 = Channel<DMA1_Channel1_BASE>;
using D1C2 = Channel<DMA1_Channel2_BASE>;
using D1C3 = Channel<DMA1_Channel3_BASE>;
using D1C4 = Channel<DMA1_Channel4_BASE>;
using D1C5 = Channel<DMA1_Channel5_BASE>;
using D1C6 = Channel<DMA1_Channel6_BASE>;
using D1C7 = Channel<DMA1_Channel7_BASE>;

using D2C1 = Channel<DMA2_Channel1_BASE>;
using D2C2 = Channel<DMA2_Channel2_BASE>;
using D2C3 = Channel<DMA2_Channel3_BASE>;
using D2C4 = Channel<DMA2_Channel4_BASE>;
using D2C5 = Channel<DMA2_Channel5_BASE>;

namespace uart {

template <uint32_t base>
struct TxChan {
    using Chan = std::enable_if_t<base == USART1_BASE, D1C4>;
};

template <>
struct TxChan<USART2_BASE> {
    using Chan = D1C7;
};

template <>
struct TxChan<USART3_BASE> {
    using Chan = D1C2;
};

template <>
struct TxChan<UART4_BASE> {
    using Chan = D2C5;
};

template <uint32_t base>
struct RxChan {
    using Chan = std::enable_if_t<base == USART1_BASE, D1C5>;
};

template <>
struct RxChan<USART2_BASE> {
    using Chan = D1C6;
};

template <>
struct RxChan<USART3_BASE> {
    using Chan = D1C3;
};

template <>
struct RxChan<UART4_BASE> {
    using Chan = D2C3;
};

}

template <uint32_t base>
using UartTxChan = typename uart::TxChan<base>::Chan;

template <uint32_t base>
using UartRxChan = typename uart::RxChan<base>::Chan;



}



#endif
